let current = null;
const SINGAL = Symbol("issingal");
function call(fn, ...args) {
  if (typeof fn === "function") {
    return call(fn.call(null, ...args), ...args);
  }
  return fn;
}
export function singal(value, options = {}) {
  const watchers = new Set();
  const get = () => {
    const _cur = current;
    if (_cur) {
      watchers.add(_cur);
      _cur.clears = _cur.clears || [];
      _cur.clears.push(() => {
        watchers.delete(_cur);
      });
    }
    return value;
  };
  const set = (newValue) => {
    newValue = call(newValue);
    if (typeof options.compare === "function") {
      if (options.compare(value, newValue) === true) {
        return;
      }
    } else {
      if (value === newValue) return;
    }
    value = newValue;

    watchers.forEach((w) => w(value));
  };
  const watch = (fn) => {
    watchers.add(fn);
    return () => {
      watchers.delete(fn);
    };
  };

  const sg = Object.create(null);
  sg.watch = watch;
  sg.watchers = watchers;

  Object.defineProperty(sg, "value", {
    get,
    set,
  });
  Object.defineProperty(sg, SINGAL, {
    value: true,
  });
  Object.freeze(sg);

  return sg;
}

export function effect(fn) {
  let _cur = current;
  current = fn;
  fn();
  current = _cur;
  return () => {
    fn.clears && fn.clears.forEach(call);
  };
}
export function isSingal(val) {
  return val[SINGAL] === true;
}
